#include <stdio.h>
#include <stdlib.h>
#include "network.h"

/** Estructura de la Network */
struct sNetwork
{
    /** Almacena la Network: en la Key de la hash se almacenan
     * los Nodos origen y en la Data una lista aristas en
     * forma de tuplas (Nodo Destino, Capacidad) */
    HASHTBL * Hash; 
     /** Tamaño de la Network: necesario para cambiar el tamaño de la Hash */
    int NetSize;
};

/** Crea la network */
Network network_create (void)
{
    Network NewNet = NULL;
    
    NewNet = (Network) malloc (sizeof (struct sNetwork));
    if(NewNet != NULL){
        NewNet->Hash = hashtbl_create (1, NULL); /* Hay que verificar esto */
        NewNet->NetSize = 0;
    }
    
    return NewNet;
}

/** Agrega los vecinos de la network */
void network_add_node (Network net, Node NodeSource, Node_tuple NodeTup)
{
	List neigh_list;
	int error;
	if (hashtbl_get (net->Hash, (const char *) &NodeSource) == NULL)
	{
		neigh_list = list_create();
		neigh_list = list_add_new_elem(neigh_list, NodeTup);
		net->NetSize++;
		error = hashtbl_resize(net->Hash, net->NetSize); 
		error = hashtbl_insert(net->Hash, (const char *) &NodeSource, neigh_list);		
	}
	else
	{
		neigh_list = hashtbl_get (net->Hash, (const char *) &NodeSource);
		if (!list_exists_elem(neigh_list, NodeTup))
		{
			neigh_list = list_add_new_elem(neigh_list, NodeTup);
		}
	}
}

/** Devuelve una lista de tuplas con los vecinos de NodeSource*/
Node* network_get_neighbours (Network net, Node NodeSource)
{
	List neigh_node;
	Node *array_fromlist = NULL;
	Tuple NodeCurrent;
	long int i;
	neigh_node = hashtbl_get(net->Hash, (const char *) &NodeSource);

	if (neigh_node != NULL) {
		array_fromlist = calloc (list_get_size(neigh_node) + 1, sizeof(long int));
		if (array_fromlist == NULL)
		{
			exit (EXIT_FAILURE);
		}
		array_fromlist[0] = list_get_size(neigh_node);
		for (i = 1; i <= array_fromlist[0]; i++)
		{
			NodeCurrent = list_get_elem(neigh_node, i);
			array_fromlist[i] = tuple_fst(NodeCurrent);
            free(NodeCurrent);
			
		}
	}
	return array_fromlist;
}


/** Devuelve la capacidad de la aristra entre dos nodos */
long int network_get_capacity (Network net, Node Node_s, Node Node_t)
{
    List neigh_node;
	Tuple NodeCurrent;
	long int i;
    long int capacity = -1; /* Si no existe arista entre Node_s y Node_t */
	neigh_node = hashtbl_get(net->Hash, (const char *) &Node_s);
    
    for (i = 0; i <= list_get_size(neigh_node); i++) 
    {
        NodeCurrent = list_get_elem(neigh_node, i);
        if (Node_t == tuple_fst(NodeCurrent))
        {
            capacity = tuple_snd(NodeCurrent);
            /*printf("capacity = %li, node current = %li\n", capacity, tuple_fst(NodeCurrent));*/
            tuple_destroy(NodeCurrent);
            break;
        }
       tuple_destroy(NodeCurrent);
    }
    return capacity;
}



/** Destruye un Network */
Network network_destroy (Network Net)
{
	hashtbl_destroy (Net->Hash);
	free(Net);
	return Net;
}

